package encrypt

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/hex"
	"errors"
	"io"
)

// AES/CBC/PKCS5模式加密
func AesCbcPkcs5Encrypt(originData string, keyStr string) (encryptText string, err error) {
	key, _ := hex.DecodeString(keyStr)
	plainText := []byte(originData)
	plainText = PKCS5Padding(plainText, aes.BlockSize)
	block, err := aes.NewCipher(key)
	if err != nil {
		return
	}
	cipherText := make([]byte, aes.BlockSize+len(plainText))
	iv := cipherText[:aes.BlockSize]
	if _, err = io.ReadFull(rand.Reader, iv); err != nil {
		return
	}
	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(cipherText[aes.BlockSize:], plainText)
	encryptText = hex.EncodeToString(cipherText)
	return
}

// AES/CBC/PKCS5模式解密
func AesCbcPkcs5Decrypt(cipherData string, keyStr string) (plainText string, err error) {
	key, _ := hex.DecodeString(keyStr)
	cipherText, _ := hex.DecodeString(cipherData)
	block, err := aes.NewCipher(key)
	if err != nil {
		return
	}
	if len(cipherText) < aes.BlockSize {
		err = errors.New("cipher text too short")
		return
	}
	iv := cipherText[:aes.BlockSize]
	cipherText = cipherText[aes.BlockSize:]
	if len(cipherText)%aes.BlockSize != 0 {
		err = errors.New("cipher text is not a multiple of the block size")
		return
	}
	mode := cipher.NewCBCDecrypter(block, iv)
	mode.CryptBlocks(cipherText, cipherText)
	cipherText = PKCS5UnPadding(cipherText)
	plainText = string(cipherText)
	return
}

func PKCS5Padding(plainText []byte, blockSize int) []byte {
	padding := blockSize - len(plainText)%blockSize
	padText := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(plainText, padText...)
}

func PKCS5UnPadding(cipherText []byte) []byte {
	length := len(cipherText)
	padding := int(cipherText[length-1])
	return cipherText[:(length - padding)]
}
